小程序核心知识点

相关资料地址:

  1. 小程序模板地址 https://gitee.com/huruqing/film-mini-template

  2. 卖座电影 https://m.maizuo.com/v5/#/films/nowPlaying

  3. 猫眼电影

    https://i.maoyan.com/#movie

  4. 有赞小程序ui库

    https://vant-contrib.gitee.io/vant-weapp/#/home

(一) 静态页面

  1. 使用小程序组件
    1. view 相当于 div
    2. text 相当于span
    3. img变为image
    4. 地图,音视频,画布等
  2. 使用 rpx 作为单位,这个屏幕的宽度为 750rpx
  3. page{} 该页面的最外层的容器
  4. 底部 tab 配置 文档=>框架=>全局配置=>tabBar
  5. 开放数据 文档=>组件=>开放能力=>open-data
  6. 编译模式, 编译模式添加启动参数
// 列表跳详情的时候, 会带上id, 比如 url="/detail/detail?id=xxxx"
// 如果此时切换到了detail页面的模式, 刷新数据就没有了
// 这个时候可以编辑detail页面的编译模式, 添加启动参数即可解决问题
  1. 在小程序中使用 vant-ui
https://vant-contrib.gitee.io/vant-weapp/#/home   // 文档地址
  1. 小程序中使用 less

(二) 生命周期

小程序有以下生命周期
  1. onLoad 生命周期函数--监听页面加载`
  2. onReady 生命周期函数--监听页面初次渲染完成
  3. onShow 生命周期函数--监听页面显示
  4. onHide 生命周期函数--监听页面隐藏
  5. onUnload 生命周期函数--监听页面卸载
  6. onPullDownRefresh 页面相关事件处理函数--监听用户下拉动作
  7. onReachBottom 页面上拉触底事件的处理函数
  8. onShareAppMessage 用户点击右上角分享
注意:
  1. onLoad 只会在小程序中执行一次,比如当前页面是 a 页面,我们在 onLoad 发送请求去获取数据,然后我们切到 b 页面,再切回 a 页面的时候,onLoad 不会再触发,也就是不会再重新发请求获取数据
  2. onShow 跟 onLoad 不同,每次切回 a 页面的时候都会触发这个生命周期函数
  3. 所以到底是在 onLoad 发请求还是在 onShow 发请求,要根据实际的业务去做

(三) 获取数据

前言
  1. 小程序只支持 https
  2. 需要到小程序后台配置域名白名单
  3. 项目中请求了非 https 和不在域名白名单上的接口会报错
  4. 开发时可以取消域名校验,就可以请求任意接口,设置方法 小程序右上角详情 =》本地设置 => 不校验合法域名...
(一) 发送请求

文档地址 文档首页 => api => 网络 => 发起请求

// 示例
wx.request({
  url: "test.php", //仅为示例,并非真实的接口地址
  // 请求参数
  data: {
    x: "",
    y: "",
  },
  method: "get", // 请求类型
  header: {
    "content-type": "application/json", // 默认值
  },
  // 成功的回调
  success(res) {
    console.log(res.data);
  },
  // 失败的回调
  fail(error) {
    console.log(error);
  },
  // 不管是成功还是失败都会调用此方法
  complete() {
    console.log("done");
  },
});
(二) 使用 promise 封装请求
  1. 配置 baseUrl
// config.js
let env = "prod";
let baseUrl = "";
if (env === "dev") {
  // 本地地址
  baseUrl = "https://localhost:3009";
} else if (env === "prod") {
  baseUrl = "https://huruqing.cn:3009";
}

// 导出
export { baseUrl };
  1. 新建/utils/reques.js 文件,内容如下
import { baseUrl } from "./config.js";

/**
 * 封装请求
 * url:请求地址
 * data:请求参数
 * method: 请求类型
 */

const request = (url, data, method) => {
  // 获取token,登录时存的
  let token = wx.getStorageSync("token");
  url = baseUrl + url;
  return new Promise((resolve, reject) => {
    // 请求
    wx.request({
      url,
      method,
      data,
      header: {
        "user-token": token,
      },
      success: (res) => {
        if (res.data.code == 666) {
          resolve(res.data);
        } else if (res.data.code == 603) {
          wx.removeStorageSync("token");
          wx.showModal({
            title: "提示",
            content: "登录已过期,是否重新登录",
            success(res) {
              if (res.confirm) {
                // 跳转到个人中心页面
                wx.switchTab({
                  url: "/pages/my/my",
                });
              } else if (res.cancel) {
                console.log("用户点击取消");
              }
            },
          });
        } else {
          reject(res.data.msg);
        }
      },
      fail: (err) => {
        reject("网络异常");
      },
    });
  });
};

const get = (url, data) => {
  return request(url, data, "get");
};

const post = (url, data) => {
  return request(url, data, "post");
};

export default {
  get,
  post,
};
  1. 挂载到 app,在页面中就不需要重复加载
// app.js

import request from "./utils/request.js";
App({
  onLaunch: function() {
    this.$get = request.get;
    this.$post = request.post;
  },
});
  1. 在页面中使用
// my.js

// 获取app对象
const app = getApp();
Page({
  onShow() {
    this.getData();
  },

  // 发送请求
  getData() {
    let url = "xxxxx";
    let data = { xxx: xxx };
    app
      .$get(url, data)
      .then((res) => {
        console.log(res);
      })
      .catch((err) => {
        console.log(err);
      });
  },
});

(四) 渲染页面

  1. data 和 setData
Page({
  data: {
    count: 1,
  },

  changeCount() {
    // 获取data里count的值
    let count = this.data.count;
    // 加1
    this.setData({
      count: ++count,
    });
  },
});
  1. 插值表达式 , wxml 中所有的变量都使用(除了wx:key)
    1. 如果数组成员是字符串或者数字, wx:key="index"或者wx:key="*this"
    2. 如果数组成员是对象, 比如[{name:'zs',id:1}],wx:key="id"
  2. 条件渲染 wx:if
  3. 列表渲染 wx:for 默认有 item 和 index
  4. 双重wx:for时需要其中一个指定 item 和 index

(五) 绑定事件

文档地址 : 文档首页 => 指南 => 事件系统

// 例子1 小程序
<view data-username="hurqing" bindtap="tapName"> Click me! </view>
Page({
  tapName: function(event) {
    // 小程序事件不能像vue那样传参,只能通过自定义属性来传参
    let username = event.target.dataset.username;
    console.log(username);
  },
});

注意: wxml和html一样, 不区分大小写, 页面上的大写, 最后变成了小写

  1. 常见事件类型 | 类型 | 触发条件 | 最低版本 | | --- | --- | --- | | touchstart | 手指触摸动作开始 | | | touchmove | 手指触摸后移动 | | | touchcancel | 手指触摸动作被打断,如来电提醒,弹窗 | | | touchend | 手指触摸动作结束 | | | tap | 手指触摸后马上离开 | | | longpress | 手指触摸后,超过 350ms 再离开,如果指定了事件回调函数并触发了这个事件,tap 事件将不被触发 | 1.5.0 | | longtap | 手指触摸后,超过 350ms 再离开(推荐使用 longpress 事件代替) | | | transitionend | 会在 WXSS transition 或 wx.createAnimation 动画结束后触发 | | | animationstart | 会在一个 WXSS animation 动画开始时触发 | | | animationiteration | 会在一个 WXSS animation 一次迭代结束时触发 | | | animationend | 会在一个 WXSS animation 动画完成时触发 | | | touchforcechange | 在支持 3D Touch 的 iPhone 设备,重按时会触发 | |

(六) 页面跳转

小程序常用两种方式,普通页面跳转和 tab 栏切换(跳转)
  1. 跳转方式一 通过组件 navigator 进行跳转,需要指定跳转类型 open-type
<view class="btn-area">
  // 普通页面跳转
  <navigator
    url="/page/navigate/navigate?title=navigate"
    hover-class="navigator-hover"
    >跳转到新页面</navigator>
  
  // 跳转到tab栏
  <navigator
    url="/page/index/index"
    open-type="switchTab"
    hover-class="other-navigator-hover">切换 Tab</navigator>

  // 重定向
  <navigator
    url="../../redirect/redirect/redirect?title=redirect"
    open-type="redirect"
    hover-class="other-navigator-hover"
    >在当前页打开</navigator
  >
  
  // 跳转到小程序
  <navigator
    target="miniProgram"
    open-type="navigate"
    app-id=""
    path=""
    extra-data=""
    version="release"
    >打开绑定的小程序</navigator
  >
</view>
  1. 跳转方式二 通过 js 进行跳转
// 普通页面跳转
wx.navigateTo({
  url: "/product/product",
});
// tab栏切换
wx.switchTab({
  url: "/index",
});

(七) 路由传参

// 列表页
 <navigator wx:for="{{flowerList}}" class="flex item pl-10 bg-fff" url="/pages/detail/detail?id={{item.id}}"></navigator>
   
// 详情页
  onLoad: function (options) {
    console.log(options.flowerId);
  },   
    
// 参数为对象时,要先将对象转成字符串
  toDetail() {
    let obj = {useranme: 'zs',age:100};
    // 将对象转成json字符串
    let query = JSON.stringify(obj);
    // 传参
    wx.navigateTo({
      url: '/pages/detail/detail?query='+query,
    }) 
  },
    
 // 获取参数
  onLoad: function (options) {
    let obj = JSON.parse(options.query);
    console.log(obj);
  },    
注意: wx.swichTab跳转url后不能带参数

(八) 数据缓存

  1. 异步方法
wx.setStorage({
  key: "key",
  data: "value",
});
wx.getStorage({
  key: "key",
  success(res) {
    console.log(res.data);
  },
});
  1. 同步方法
wx.setStorageSync("key", "value");

// 例子
let token = 'asdfasdfasjdflasdjf;asdf;asdfjsak;ldf';
wx.setStorageSync('token',token);

let token = wx.getStorageSync('token');

(九) 用户授权

知识点

  1. 用户授权: 录音授权
  2. 检查用户是否已经授权
  3. 获取用户信息的授权操作

用户授权功能列表 除了开放数据以外,小程序的很多功能都需要获得用户授权,才可以进行下一步的操作, 用户授权的功能列表

(1) 用户授权之录音授权

  onLoad: function (options) {
    wx.authorize({
      // 授权类型
      scope: "scope.record", // scope.camera 摄像头
      success() {
        // 用户已经同意小程序使用录音功能,才能调用录音相关接口
        console.log('授权成功');
      },
      fail() {
        console.log('想要使用录音功能, 请先授权');
      }
    });
  }

// 开始录音

Page({ 
  onLoad: function (options) {
    let obj = wx.getRecorderManager();
    obj.start({
      format: 'mp3'
    });
    obj.onStart((aa) => {
      console.log('开始录音');
    })
    setTimeout(() => {
      obj.stop();
      obj.onStop(res => {
        console.log(res);
        console.log('停止录音')
      })
    }, 3000)
  }, 
})

(2) 获取用户信息的授权

  1. scope.userInfo获取用户信息的授权无法通过js直接调起授权窗口
  2. 必须绑定点击事件, 用户去点击才能调起授权, 我想应该是腾讯想提醒用户要慎重
// demo.wxml
<button bindtap="getUserProfile"> 获取用户信息 </button>

// demo.js
getUserProfile(e) {  
  wx.getUserProfile({
    desc: '用于完善会员资料',  
    success: (res) => {
      console.log(res); 
    }
  })
},

(3) 获取用户的授权信息

获取用户的当前设置。返回值中只会出现小程序已经向用户请求过的权限

wx.getSetting({
    success (res) {
        console.log(res.authSetting) 
    }
})

(十) 小程序登录

前言

在做小程序电商项目的时候,添加一个商品到购物车,后台需要知道是哪个用户执行了添加的操作,这里就涉及到了用户识别的操作.

整个流程如下:
  1. 前端调用后台的登录接口,获得 token
  2. 前端发送请求带上 token,后台可以通过 token 来识别用户
在第一步中,有分为以下几个步骤
  1. 前端首先调用 wx.login 获取 code,
  2. 紧接着需要调用 wx.getUserInfo 来获得用户信息 (需要用户授权) ,获得 iv 和 encryptedData
  3. 调用后台登录接口,比如 /user/login,把以上三个参数
{
  code, iv, encryptedData;
}
  1. 传给后台
  2. 后台根据上面三个参数调用微信接口获得 openid,再将 openid 和用户的其他信息一并存起来,同时生成 userId,根据 userId 生成一个加密的 token 返回给小程序,小程序端再把 token 放入请求头,每次发请求都带上,这样后台就可以识别用户了
import { baseUrl } from "./config.js"; 
const request = (url, data, method) => {
  // 获取token,登录时存的
  let token = wx.getStorageSync("token");
  url = baseUrl + url;
  return new Promise((resolve, reject) => {
    // 请求
    wx.request({
      url,
      method,
      data,
      // 把token加入到请求头
      header: {
        "user-token": token,
      },
      success: (res) => { 
        if (res.data.code == 666) {
          resolve(res.data);
        } else if (res.data.code == 603) {
          wx.removeStorageSync("token");
          wx.showModal({
            title: "提示",
            content: "登录已过期,是否重新登录",
            success(res) {
              if (res.confirm) {
                // 跳转到个人中心页面
                wx.switchTab({
                  url: "/pages/my/my",
                });
              } else if (res.cancel) {
                console.info("用户点击取消");
              }
            },
          });
        } else {
          // wx.dialog(res.data.msg);
          wx.showToast({
            title: res.data.msg,
            icon: 'none',
            duration: 1500
          }) 
        }
      },
      fail: (err) => { 
        wx.showToast({
          title: '网络异常',
          icon: 'none',
          duration: 1500
        })
      },
    });
  });
};

const get = (url, data) => {
  return request(url, data, "get");
};

const post = (url, data) => {
  return request(url, data, "post");
};

export default {
  get,
  post,
};

(十一) 小程序获取位置

(1) 获取位置信息

  1. 文档资料

    • 小程序文档->api->位置
    • [腾讯地图API](平时的开发也可以使用百度地图,高度地图):
  2. 添加密钥

    使用位置服务需要先添加密钥, [链接地址]

  3. 获取经纬度

wx.getLocation({
  type: 'wgs84',
  success: (res) => {
    const latitude = res.latitude;
    const longitude = res.longitude;
  },
  fail: (err) => {
    this.setData({
      city: '定位失败,请手动选择'
    })
  }
})

(2) 实际应用:

  1. 获取城市名称(需要上一点提供的经纬度)

     wx.request({
       url: `https://apis.map.qq.com/ws/geocoder/v1/?location=${latitude},${longitude}&key=XBZBZ-OBG63-LOD3N-3QR5Q-X6Z2Q-BFBIR`,
       success: (res) => {
         let cityName = res.data.result.address_component.city;
         console.log(cityName)
       }
     })
    
  2. 计算两个坐标之间的距离

(十二) 支付流程

  1. 前端请求后端创建订单接口
  2. 后端调用小程序统一下单 API, 成功之后给前端返回支付所需参数
  3. 小程序端使用 wx.requestPayment调起支付窗口
<button bindtap="payNow">立即支付</button>

payNow() {
	  wx.requestPayment({ 
      timeStamp: '时间戳,后台返回', 
      nonceStr: '随机字符串,后台返回', 
      package: '统一下单接口返回的 prepay_id 参数值,后台返回',
      signType: '签名类型,后台返回',
      paySign: '签名,后台返回',
      // 支付成功的回调
      success(res) {
        console.log('支付成功');
      },
      // 支付失败的回调
      fail(err) {
        console.log('支付失败');
      },
    }); 
}

(十三) 自定义组件

自定义一个导航组件

  1. 新建nav文件夹, 右键nav, 新建组件

  2. 编写组件静态

  3. 在父组件(demo.json) 注册组件, 添加代码

    {
      "usingComponents": {
        "Nav":"./nav/nav"
      }
    }
    
  4. 在wxml中使用组件

    <Nav></Nav>
    
  5. 父子组件通信

    1. 父传子, 使用属性, 子组件通过 properties:{} 接收
    2. 子传父
      1. 父组件绑定自定义事件 bind:xxx
      2. 子组件触发自定事件xxx this.triggerEvent('xxx',{数据对象})

完整代码:

父组件

// demo.wxml
<Nav title="购物车" bind:clickLeft="clickLeft"></Nav>

// demo.json
{
  "usingComponents": {
    "Nav":"./nav/nav"
  }
}
// demo.js
Page({ 
  clickLeft() {
    console.log('左按钮被点击了')
  }
})

子组件

// nav.wxml
<view class="header">
  <text bindtap="clickLeft">左按钮</text>
  <view class="title">{{title}}</view>
  <text class="button">右按钮</text>
</view>

// nav.js
Component({ 
  properties: {
    title: String
  }, 
  methods: {
    clickLeft() {
      this.triggerEvent('clickLeft');
  },  
}) 

// nav.wxss
page {
  background: #ebebeb;
}
.header {
  display: flex;
  height: 50px;
  background-color: green;
  justify-content: space-between;
  align-items: center;
  padding: 0 15px;
  color: #fff;
}